#include <wm.h>
#include <ipc.h>
#include <stdio.h>
#include <malloc.h>
#include <gato/surface.h>
#include <syscall.h>

static ipc_struct_t *wm_client_ipc = &(ipc_struct_t){};

static inline ipc_struct_t *get_wm_ipc()
{
    return wm_client_ipc;
}

int wm_window_poll_event(window_t *win, wm_event_t *e, kduration_t *timeout)
{
    struct kobj_poll_desc_t desc = {
        .slot = win->connect.event,
        .signals = 1,
    };
    usys_object_wait_many(&desc, 1, timeout);
    int ret = usys_fifo_read(win->connect.event, e, sizeof(wm_event_t));
    return ret;
}

window_t *wm_window_create(int x, int y, int width, int height, color_t bg)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_CREATE), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_CREATE;
    GET_WM_REQUEST(param, WM_WINDOW_CREATE, fr);

    param->x = x;
    param->y = y;
    param->width = width;
    param->height = height;
    param->bg = bg;

    wid_t ret = ipc_call(get_wm_ipc(), msg);

    window_t *win = (window_t *)calloc(1, sizeof(window_t));
    win->connect = param->connect;
    win->id = ret;

    void *addr = usys_vmo_map(win->connect.vmo, NULL, PROT_READ | PROT_WRITE, 0);
    surface_wrap(win->connect.surface, (color_t *)addr, width, height);
    ipc_destroy_msg(msg);
    return win;
}

void wm_window_destory(window_t *win)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_DESTORY), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_DESTORY;
    GET_WM_REQUEST(param, WM_WINDOW_DESTORY, fr);

    param->id = win->id;

    ipc_call(get_wm_ipc(), msg);

    free(win);
    ipc_destroy_msg(msg);
}
bool_t wm_window_config_set(window_t *win, struct window_config_t *config)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_CONFIG_SET), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_CONFIG_SET;
    GET_WM_REQUEST(param, WM_WINDOW_CONFIG_SET, fr);

    param->id = win->id;
    param->config = *config;

    bool_t ret = ipc_call(get_wm_ipc(), msg);
    ipc_destroy_msg(msg);
    return ret;
}
bool_t wm_window_config_get(window_t *win, struct window_config_t *config)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_CONFIG_GET), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_CONFIG_GET;
    GET_WM_REQUEST(param, WM_WINDOW_CONFIG_GET, fr);

    param->id = win->id;

    bool_t ret = ipc_call(get_wm_ipc(), msg);

    *config = param->config;

    ipc_destroy_msg(msg);
    return ret;
}

void wm_window_event_mask_set(window_t *win, uint64_t event_mask)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_EVENT_MASK_SET), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_EVENT_MASK_SET;
    GET_WM_REQUEST(param, WM_WINDOW_EVENT_MASK_SET, fr);

    param->id = win->id;
    param->event_mask = event_mask;

    ipc_call(get_wm_ipc(), msg);
    ipc_destroy_msg(msg);
}

bool_t wm_window_show(window_t *win)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_SHOW), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_SHOW;
    GET_WM_REQUEST(param, WM_WINDOW_SHOW, fr);

    param->id = win->id;

    bool_t ret = ipc_call(get_wm_ipc(), msg);
    ipc_destroy_msg(msg);
    return ret;
}
bool_t wm_window_hide(window_t *win)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_HIDE), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_HIDE;
    GET_WM_REQUEST(param, WM_WINDOW_HIDE, fr);

    param->id = win->id;

    bool_t ret = ipc_call(get_wm_ipc(), msg);
    ipc_destroy_msg(msg);
    return ret;
}
void wm_window_update(window_t *win)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_UPDATE), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_UPDATE;
    GET_WM_REQUEST(param, WM_WINDOW_UPDATE, fr);

    param->id = win->id;

    ipc_call(get_wm_ipc(), msg);
    ipc_destroy_msg(msg);
}
void wm_window_raise(window_t *win)
{
    ipc_msg_t *msg = ipc_create_msg(get_wm_ipc(), GET_WM_REQUEST_SIZE(WM_WINDOW_RAISE), 0);
    struct wm_request *fr = (struct wm_request *)ipc_get_msg_data(msg);
    fr->req = WM_WINDOW_RAISE;
    GET_WM_REQUEST(param, WM_WINDOW_RAISE, fr);

    param->id = win->id;

    ipc_call(get_wm_ipc(), msg);
    ipc_destroy_msg(msg);
}

void do_wm_init(void)
{
    printf("[wm client]\n");
    ipc_register_client_by_name("wm", wm_client_ipc);
}